﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;

namespace OpenWaves.ActivityFeed
{
    public class UpdatesGenerator
    {
        #region properties

        private readonly IActivityFeedService _activityFeedService;

        private readonly SentenceGenerator _sentenceGenerator;        

        #endregion                

        public UpdatesGenerator(string fileName): this(fileName, ServiceLocator.Resolve<IActivityFeedService>())
        {            
        }

        public UpdatesGenerator(string fileName, IActivityFeedService activityFeedService)
        {
            this._randomizer = new Random((int)DateTime.Now.Ticks);

            this._sentenceGenerator = new SentenceGenerator();
            this._sentenceGenerator.Initialize(fileName);

            this._activityFeedService = activityFeedService;
        }

        private readonly Random _randomizer;

        private string GetSentences(DataRange<int> sentencesCountRange)
        {
            var sentencesCount = this.GetRandomFromRange(sentencesCountRange);

            var sb = new StringBuilder();
            for (int i = 0; i < sentencesCount; i++)
            {
                sb.Append(this._sentenceGenerator.GetNextSentence()).Append(" ");
            }

            return sb.ToString().Trim();
        }

        private int GetRandomNumber(int maxValue)
        {
            return this._randomizer.Next(0, maxValue);            
        }

        private int GetRandomFromRange(DataRange<int> range)
        {
            if (range.IsEmpty)
            {
                return range.MinValue;
            }
            return this._randomizer.Next(range.MinValue, range.MaxValue);            
        }


        public void GenerateUpdates(UpdatesGeneratorOptions options)
        {           
            using (var fakeTime = FakeTimeScope.Begin())
            {
                for (var i = 0; i < options.NumberOfUpdates; i++)
                {
                    fakeTime.Now = fakeTime.Now.AddMinutes(-this.GetRandomFromRange(options.MinutesDalayBetweenUpdates));

                    var post = this.GetSentences(options.NumberOfSentencesForUpdate);
                    
                    var user = options.Users[this.GetRandomNumber(options.Users.Count)];
                    var topics = new List<Topic>() { Topic.FromUserId(user) };
                    if (options.Topics.Count > 0)
                    {
                        var topicsToAdd = this.GetRandomNumber(options.Topics.Count);
                        for (var topicIdx = 0; topicIdx < topicsToAdd; topicIdx++)
                        {
                            var topic = options.Topics[this.GetRandomNumber(options.Topics.Count)];
                            if (topics.All(t => t != topic))
                            {
                                topics.Add(topic);
                            }
                        }
                    }
                    var update = this._activityFeedService.PublishPublicUpdate(new Post(post), user, "web", topics);

                    var now = Time.Now;
                    int numberOfComments = this.GetRandomFromRange(options.NumberOfCommentsForUpdate);
                    for (var j = 0; j < numberOfComments; j++)
                    {
                        var comment = this.GetSentences(options.NumberOfSentencesForComment);
                        user = options.Users[this.GetRandomNumber(options.Users.Count)];

                        fakeTime.Now = update.PublishedOn.AddMinutes(this.GetRandomNumber(100));
                        update.AddComment(comment, user);
                    }
                    fakeTime.Now = now;
                }
            }
        }
    }

    public class SentenceGenerator
    {
        private string _text;
        private int _startIndex;

        [SuppressMessage("Microsoft.Globalization", "CA1309:Use ordinal StringComparison")]
        [SuppressMessage("Microsoft.Design", "CA1024:Use properties where appropriate")]
        public string GetNextSentence()
        {
            if ( this._startIndex + 1 >= this._text.Length)
            {
                this._startIndex = 0;
            }

            var nextIndex = this._text.IndexOf(".", this._startIndex + 1, StringComparison.InvariantCulture);

            if (nextIndex < 0)
            {
                this._startIndex = 0;
                nextIndex = this._text.IndexOf(".", this._startIndex + 1, StringComparison.InvariantCulture);
            }

            // if the sentence is too short then get next
            if ( nextIndex - this._startIndex + 1 < 7)
            {
                this._startIndex = nextIndex + 1;
                return this.GetNextSentence();
            }

            var result = this._text.Substring(this._startIndex, nextIndex - this._startIndex + 1);
            this._startIndex = nextIndex + 1;

            result = result[0].ToString(CultureInfo.InvariantCulture).ToUpperInvariant() + result.Substring(1);

            return result;
        }

        public void Initialize(string fileName)
        {
            this._startIndex = 0;
            this._text = File.ReadAllText(fileName).Replace(Environment.NewLine, " ").Replace("  ", " ");
        }
    }


    public class UpdatesGeneratorOptions
    {
        public UpdatesGeneratorOptions()
        {
            this.Topics = new List<Topic>();
            this.Users = new List<string>();
            this.NumberOfUpdates = 100;
            this.NumberOfSentencesForUpdate = new DataRange<int>(1, 5);
            this.NumberOfCommentsForUpdate = new DataRange<int>(0, 5);
            this.NumberOfSentencesForComment = new DataRange<int>(1, 5);
            this.MinutesDalayBetweenUpdates = new DataRange<int>(1,30);
        }

        public int NumberOfUpdates { get; set; }
        public DataRange<int> NumberOfSentencesForUpdate{ get; private set; }
        [SuppressMessage("Microsoft.Naming", "CA1704:Identifiers should be spelled correctly")]
        public DataRange<int> MinutesDalayBetweenUpdates { get; private set; }
        public DataRange<int> NumberOfCommentsForUpdate{ get; private set; }
        public DataRange<int> NumberOfSentencesForComment{ get; private set; }
        [SuppressMessage("Microsoft.Design", "CA1002:Do not expose generic lists")]
        public List<Topic> Topics { get; private set; }
        [SuppressMessage("Microsoft.Design", "CA1002:Do not expose generic lists")]
        public List<string> Users { get; private set; }
    }

    public class DataRange<T> where T: IComparable
    {
        public DataRange()
        {
            
        }

        public DataRange(T minValue, T maxValue)
        {
            this.MinValue = minValue;
            this.MaxValue = maxValue;
        }

        public T MinValue { get; set; }
        public T MaxValue { get; set; }

        public bool IsEmpty
        {
            get { return this.MinValue.CompareTo(this.MaxValue) == 0; }
        }
    }
}